package io.gitee.duxiuhuan.mailbox.service.impl;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import io.gitee.duxiuhuan.mailbox.database.entries.MessageInfo;
import io.gitee.duxiuhuan.mailbox.service.entries.QuestionResult;
import io.gitee.duxiuhuan.mailbox.service.entries.aiRequestResultEntries.Choice;
import io.gitee.duxiuhuan.mailbox.service.entries.aiRequestResultEntries.AIRequestResult;
import io.gitee.duxiuhuan.mailbox.service.entries.aiRequestResultEntries.Chunk;
import io.gitee.duxiuhuan.mailbox.service.inter.AIRequest;
import io.gitee.duxiuhuan.mailbox.service.inter.MessageManager;
import io.gitee.duxiuhuan.mailbox.utils.IdUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Log4j2
@Service
public class AIRequestService implements AIRequest<AIRequestResult<Choice>> {
    
    @Autowired
    private RestTemplate restTemplate;
    
    @Autowired
    private WebClient.Builder webClientBuilder;
    
    @Autowired
    private MessageManager messageManager;
    
    @Value("${mailbox.apikeyList}")
    private String apiKeyList;
    
    @Value("${mailbox.urlList}")
    private String urlList;
    
    @Value("${mailbox.build-request-body.is-reserver:true}")
    private Boolean isReserver;
    @Override
    public AIRequestResult<Choice> request(String model, List<MessageInfo> messageInfoList) {
        String requestBody = buildRequestBody(model, messageInfoList, false);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        headers.set("Authorization", "Bearer " + this.splitStringForModel(model,apiKeyList));
        HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
        
        ResponseEntity<String> response = restTemplate.exchange(this.splitStringForModel(model, urlList), HttpMethod.POST, entity, String.class);
        
        if (response.getStatusCode().is2xxSuccessful()) {
            try {
                ObjectMapper objectMapper = new ObjectMapper();
                return objectMapper.readValue(response.getBody(), new TypeReference<AIRequestResult<Choice>>() {});
            } catch (Exception e) {
                log.error("AI接口返回结果初始化失败，返回结果为：" + response.getBody() + "json解析目标实体类为DeepseekResult");
                e.printStackTrace();
            }
        } else {
            throw new RuntimeException("调用Deepseek接口失败，错误状态码为：" + response.getStatusCode() + "错误信息为" + response.getBody());
        }
        return null;
    }
    
    @Override
    public StreamingResponseBody requestStream(String model, List<MessageInfo> messageInfoList, Long userId,String tableName) {
        //请求通过webClient发起
        WebClient webClient = webClientBuilder.baseUrl(this.splitStringForModel(model,urlList)).build();
        
        //构件requestBody
        String requestBody = buildRequestBody(model, messageInfoList, true);
        
        //构件messageInfo，用于请求结束后将结果插入数据库
        MessageInfo messageInfo = messageInfoList.get(messageInfoList.size() - 1);
        List<MessageInfo> messageInfos = new ArrayList<>();
        MessageInfo systemMessageInfo = new MessageInfo();
        systemMessageInfo.setId(IdUtils.generateId());
        systemMessageInfo.setCreateTime(new Date());
        systemMessageInfo.setUserOrSystem(0);
        systemMessageInfo.setSessionId(messageInfo.getSessionId());
        systemMessageInfo.setModel(model);
        systemMessageInfo.setUserId(userId);
        messageInfos.add(messageInfo);
        
        //用户将请求的流式结果组成一个字符串
        StringBuilder stringBuilder = new StringBuilder();
        
        //用于解析请求到的json结果
        ObjectMapper objectMapper = new ObjectMapper();
        
        //返回的箭头函数通过StreamingResponseBody类型实例接收，就可以轻松实现流式传输
        return outputStream -> {
            //开始发起请求
            webClient.post()
                    .header("Authorization", "Bearer " + this.splitStringForModel(model, apiKeyList))
                    .accept(MediaType.TEXT_EVENT_STREAM)
                    .contentType(MediaType.APPLICATION_JSON)
                    .bodyValue(requestBody)
                    .retrieve()
                    .bodyToFlux(String.class)
                    //处理前先过滤胰一下请求结果，排除结束的[done]，防止报错
                    .flatMap(line -> {
                        String trimmedLine = line.trim();
                        if ("[DONE]".equals(trimmedLine)) {
                            return Mono.empty();
                        } else{
                            try {
                                AIRequestResult<Chunk> chunk = objectMapper.readValue(trimmedLine, new TypeReference<AIRequestResult<Chunk>>() {});
                                return Mono.just(chunk);
                            } catch (Exception e) {
                                log.error("JSON解析失败: " + e.getMessage());
                                return Mono.empty();
                            }
                        }
                    })
                    //处理结果，返回给客户端
                    .doOnNext(chunk -> {
                        try {
                            String contentChunk = chunk.getChoices().get(0).getDelta().getContent();
                            stringBuilder.append(contentChunk);
                            QuestionResult questionResult = new QuestionResult();
                            questionResult.setTableName(tableName);
                            MessageInfo data = new MessageInfo();
                            data.setId(systemMessageInfo.getId());
                            data.setSessionId(systemMessageInfo.getSessionId());
                            data.setCreateTime(systemMessageInfo.getCreateTime());
                            data.setUserOrSystem(systemMessageInfo.getUserOrSystem());
                            data.setContent(contentChunk);
                            questionResult.setData(data);
                            outputStream.write(objectMapper.writeValueAsBytes(questionResult));
                            outputStream.flush();
                        } catch (Exception e) {
                            System.err.println("写入输出流失败: " + e.getMessage());
                        }
                    })
                    .doOnError(error -> {
                        try {
                            outputStream.write(("Error: " + error.getMessage()).getBytes(StandardCharsets.UTF_8));
                        } catch (Exception ex) {
                            throw new RuntimeException("AI接口请求错误", ex);
                        }
                    })
                    //完成后继续构件messageInfo，并将结果插入数据库
                    .doOnComplete(() -> {
                        QuestionResult questionResult = new QuestionResult();
                        questionResult.setTableName(tableName);
                        MessageInfo data = new MessageInfo();
                        data.setId(systemMessageInfo.getId());
                        data.setSessionId(systemMessageInfo.getSessionId());
                        data.setCreateTime(systemMessageInfo.getCreateTime());
                        data.setUserOrSystem(systemMessageInfo.getUserOrSystem());
                        data.setContent("done");
                        questionResult.setData(data);
                        try{
                            outputStream.write(objectMapper.writeValueAsBytes(questionResult));
                            outputStream.flush();
                        }catch (Exception e){
                            log.error("AI接口请求错误", e);
                            throw new RuntimeException("AI接口请求错误", e);
                        }
                        systemMessageInfo.setContent(stringBuilder.toString());
                        messageInfos.add(systemMessageInfo);
                        if (!messageManager.insertMessages("table_" + userId, messageInfos)){
                            log.error("更新数据库失败");
                            throw new RuntimeException("更新数据库失败");
                        }
                    }).blockLast();
        };
    }
    
    private String buildRequestBody(String model, List<MessageInfo> messageInfoList, Boolean isStream) {
        StringBuilder requestBody = new StringBuilder("{\n" + "  \"model\": \"" + model + "\",\n" + "  \"messages\": [\n");
        //部分官网提供的Api调用，信息是正向，部分是反向
        if(isReserver || isReserver.equals("true")){
            for (int i = messageInfoList.size()-1; i >= 0; i--) {
                MessageInfo messageInfo = messageInfoList.get(i);
                String role = messageInfo.getUserOrSystem() == 0 ? "system" : "user";
                // 转义 content 中的换行符和双引号
                String content = messageInfo.getContent()
                        .replace("\\", "\\\\")  // 转义反斜杠
                        .replace("\n", "\\n")   // 转义换行符
                        .replace("\"", "\\\""); // 转义双引号
                
                if (i == 0) {
                    requestBody.append("    {\"role\": \"").append(role).append("\", \"content\": \"").append(content).append("\"}\n");
                } else {
                    requestBody.append("    {\"role\": \"").append(role).append("\", \"content\": \"").append(content).append("\"},\n");
                }
            }
        }else{
            for (int i =0 ; i <= messageInfoList.size()-1; i++) {
                MessageInfo messageInfo = messageInfoList.get(i);
                String role = messageInfo.getUserOrSystem() == 0 ? "system" : "user";
                // 转义 content 中的换行符和双引号
                String content = messageInfo.getContent()
                        .replace("\\", "\\\\")  // 转义反斜杠
                        .replace("\n", "\\n")   // 转义换行符
                        .replace("\"", "\\\""); // 转义双引号
                
                if (i == messageInfoList.size()-1) {
                    requestBody.append("    {\"role\": \"").append(role).append("\", \"content\": \"").append(content).append("\"}\n");
                } else {
                    requestBody.append("    {\"role\": \"").append(role).append("\", \"content\": \"").append(content).append("\"},\n");
                }
            }
        }
        requestBody.append("  ],\n").append("  \"stream\": ").append(isStream).append("\n").append("}");
        return requestBody.toString();
    }
    
    private String splitStringForModel (String model,String list) {
        String[] split = list.split(";");
        String result = "";
        for (String itemlist : split) {
            if(itemlist.contains(model)){
                result = itemlist.split(":",2)[1];
            }
            break;
        }
        if(StringUtils.isNotBlank(result)){
            return result;
        }else {
            throw new RuntimeException("模型" + model + "缺少apikey或url");
        }
    }

}
